package api_docker

import (
	"bufio"
	"context"
	"encoding/json"
	"errors"
	"github.com/gin-gonic/gin"
	"io"
	"net/http"
	"sort"
	"strconv"
	"wrblog-api-go/pkg/docker"
	"wrblog-api-go/pkg/request"
	"wrblog-api-go/pkg/result"
	"wrblog-api-go/pkg/status"
)

// ListContainers - GET /demo/docker/containers?all=true
func ListContainers(c *gin.Context) {
	all := c.DefaultQuery("all", "false") == "true"
	name := c.DefaultQuery("name", "")
	containers, err := docker.ListContainersByFilter(context.Background(), all, name)
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	order := map[string]int{
		"running": 1,
		"paused":  2,
		"exited":  3,
	}
	// 按 State 升序排序
	sort.Slice(containers, func(i, j int) bool {
		return order[containers[i].State] < order[containers[j].State]
	})
	result.Ok(containers).Json(c)
}

// CreateContainerReq 请求体
type CreateContainerReq struct {
	Image string   `json:"image" binding:"required"`
	Cmd   []string `json:"cmd"`
	Name  string   `json:"name"`
	Env   []string `json:"env"`
}

// CreateContainer - POST /demo/docker/containers
func CreateContainer(c *gin.Context) {
	raw, _ := c.Get(request.BindObj)
	req := raw.(CreateContainerReq)
	id, err := docker.CreateNewContainer(context.Background(), req.Image, req.Cmd, req.Name, req.Env)
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	result.Ok(id).Json(c)
}

// StartContainer - POST /demo/docker/containers/:id/start
func StartContainer(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		result.Wrap(status.BadRequest, "参数解析失败！").Json(c)
		return
	}
	timeoutSec, _ := strconv.Atoi(c.DefaultQuery("timeout", "10"))
	err := docker.StartContainerByID(context.Background(), id, timeoutSec)
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	result.Ok(id).Json(c)
}

// StopContainer - POST /demo/docker/containers/:id/stop
func StopContainer(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		result.Wrap(status.BadRequest, "参数解析失败！").Json(c)
		return
	}
	timeoutSec, _ := strconv.Atoi(c.DefaultQuery("timeout", "10"))
	err := docker.StopContainerByID(context.Background(), id, timeoutSec)
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	result.Ok(id).Json(c)
}

// RemoveContainer - DELETE /demo/docker/containers/:id?force=true
func RemoveContainer(c *gin.Context) {
	id := c.Param("id")
	force := c.DefaultQuery("force", "false") == "true"
	if id == "" {
		result.Wrap(status.BadRequest, "参数解析失败！").Json(c)
		return
	}
	err := docker.RemoveContainerByID(context.Background(), id, force)
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	result.Ok(id).Json(c)
}

// ContainerLogs - GET /demo/docker/containers/:id/logs?follow=true&tail=200
func ContainerLogs(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		result.Wrap(status.BadRequest, "参数解析失败！").Json(c)
		return
	}

	// 获取参数
	follow := c.DefaultQuery("follow", "false") == "true"
	tail := c.DefaultQuery("tail", "200")

	// 获取 Docker 日志流
	rc, err := docker.ContainerLogsReader(context.Background(), id, follow, tail)
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	defer rc.Close()

	// 设置响应头
	c.Writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
	c.Writer.Header().Set("Transfer-Encoding", "chunked") // chunked streaming
	c.Status(http.StatusOK)

	// 获取 flusher 确保流式输出
	flusher, ok := c.Writer.(http.Flusher)
	if !ok {
		result.Fail(errors.New("streaming not supported")).Json(c)
		return
	}

	// 按行读取并实时写入
	scanner := bufio.NewScanner(rc)
	for scanner.Scan() {
		line := scanner.Text() + "\n"
		_, err := c.Writer.Write([]byte(line))
		if err != nil {
			// 写入失败直接退出
			break
		}
		flusher.Flush() // 立即推送到前端
	}

	// 处理扫描错误
	if err := scanner.Err(); err != nil && err != io.EOF {
		c.Writer.Write([]byte("❌ Error reading logs: " + err.Error()))
		flusher.Flush()
	}
}

// ListImages - GET /demo/docker/images
func ListImages(c *gin.Context) {
	list, err := docker.ListImages(context.Background())
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	result.Ok(list).Json(c)
}

// PullImageReq
type PullImageReq struct {
	Image string `json:"image" binding:"required"` // e.g. "alpine:latest"
}

// PullImage - POST /demo/docker/images/pull
func PullImage(c *gin.Context) {
	raw, _ := c.Get(request.BindObj)
	req := raw.(PullImageReq)
	rc, err := docker.PullImage(context.Background(), req.Image)
	if err != nil {
		result.Fail(err).Json(c)
		return
	}
	defer rc.Close()

	dec := json.NewDecoder(rc)
	var rawMsg json.RawMessage
	var out []map[string]any
	for {
		if err := dec.Decode(&rawMsg); err != nil {
			if err == io.EOF {
				break
			}
			break
		}
		var m map[string]any
		if err := json.Unmarshal(rawMsg, &m); err == nil {
			out = append(out, m)
		}
	}
	mapReq := map[string]any{
		"status":   "pulled",
		"image":    req.Image,
		"progress": out,
	}
	result.Ok(mapReq).Json(c)
}
